-module(cus_redis).
-author("datian").

-include("cus.hrl").
-include("logger.hrl").
-include("ejabberd.hrl").
-include("cus_pb.hrl").
%% API
-export([start/5]).

-export([
    get_conversation_info/1,
    get_conversation_ids/1,
    get_user_info/1,
    get_message/1,
    del_message/1,
    set_conversation_info/1,
    set_user_info/1,
    set_message/1,
    get_sys_emotion_packets/0,
    get_sys_emotion_packet/1,
    flush_all/0,
    flush_db/0,
    get_value/1,
    get_values/2,
    set_value/2,
    keys/1
        ]).


-define(PROCNAME, 'cus_redis_').


start(Host, Port, DB, Pwd, Size) ->
    lists:foreach(fun(Index) ->
                          case eredis:start_link(Host, Port, DB, Pwd) of
                              {ok, Client} ->
                                  ProcName = get_proc_name(Index),
                                  register(ProcName, Client),
                                  {ok, Client};
                              {error, _} = Err ->
                                  Err
                          end
                  end, lists:seq(1, Size)),
    ?INFO_MSG("cus redis start success", []).



get_proc_name(Index) ->
    Name = erlang:atom_to_list(?PROCNAME) ++ erlang:integer_to_list(Index),
    erlang:list_to_atom(Name).


get_redis_proc(Key) ->
    Size = cus_config:redis_size(),
    Index = erlang:phash(Key, Size),
    get_proc_name(Index).


get_conversation_info(Cid) when is_list(Cid) ->
    get_conversation_info(?l2b(Cid));
get_conversation_info(Cid) ->
    Key = <<"cus:conversation:", Cid/binary>>,
    to_term(get_value(Key)).


get_conversation_ids(Match) when is_list(Match) ->
    get_conversation_ids(?l2b(Match));
get_conversation_ids(Match) ->
    KeyMatch = <<"cus:conversation:", Match/binary>>,
    Keys = keys(KeyMatch),
    [Key -- "cus:conversation:" || Key <- Keys].


get_user_info(Uid) when is_list(Uid) ->
    get_user_info(?l2b(Uid));
get_user_info(Uid) ->
    Key = <<"cus:user:", Uid/binary>>,
    to_term(get_value(Key)).


get_message(Mid) when is_list(Mid) ->
    get_message(?l2b(Mid));
get_message(Mid) ->
    Key = <<"cus:message:", Mid/binary>>,
    to_term(get_value(Key)).


set_conversation_info(Info) ->
    Cid = ?l2b(Info#cus_conversation.cid),
    Key = <<"cus:conversation:", Cid/binary>>,
    set_value(Key, term_to_binary(Info)).


set_user_info(Info) ->
    Uid = ?l2b(Info#cus_user.uid),
    Key = <<"cus:user:", Uid/binary>>,
    set_value(Key, term_to_binary(Info)).


set_message(Info) ->
    Mid = ?l2b(Info#msg.mid),
    Key = <<"cus:message:", Mid/binary>>,
    set_value(Key, term_to_binary(Info)).


del_message(Mid) when is_list(Mid) ->
    del_message(?l2b(Mid));
del_message(Mid) ->
    Key = <<"cus:message:", Mid/binary>>,
    del(Key).


get_sys_emotion_packets() ->
    Match = <<"cus:sys_emotion_packet:*">>,
    Keys = keys(Match),
    get_values(Keys, []).


get_sys_emotion_packet(ID) when is_list(ID) ->
    get_sys_emotion_packet(?l2b(ID));
get_sys_emotion_packet(ID) ->
    Key = <<"cus:sys_emotion_packet:", ID/binary>>,
    to_term(get_value(Key)).


del(Key) ->
    ProcName = get_redis_proc(Key),
    safe_q(ProcName, ["DEL", Key]).


get_value(Key) ->
    ProcName = get_redis_proc(Key),
    safe_q(ProcName, ["GET", Key]).


set_value(Key, Value) ->
    ProcName = get_redis_proc(Key),
    safe_q(ProcName, ["SET", Key, Value]).


get_values([], Acc) ->
    Acc;
get_values([Key | T], Acc) ->
    case to_term(get_value(Key)) of
        undefined ->
            get_values(T, Acc);
        Term ->
            get_values(T, [Term | Acc])
    end.



keys(Match) ->
    ProcName = get_redis_proc(Match),
    [?b2l(Item) || Item <- safe_q(ProcName, ["KEYS", Match])].
    

flush_all() ->
    safe_q('cus_redis_1', ["FLUSHALL"]).


flush_db() ->
    safe_q('cus_redis_1', ["FLUSHDB"]).


safe_q(ProcName, Cmd) ->
    case whereis(ProcName) of
        undefined ->
            [Host, Port, DB, Pwd, _Size] = cus_config:redis(),
            case eredis:start_link(?list(Host), Port, DB, ?list(Pwd)) of
                {ok, Client} ->
                    register(ProcName, Client),
                    case eredis:q(ProcName, Cmd) of
                        {ok, undefined} ->
                            <<>>;
                        {ok, V} ->
                            V;
                        _Err -> <<>>
                    end;
                {error, Err} ->
                    ?ERROR_MSG("Failed to start redis client: ~p", [Err]),
                    <<>>
            end;
        _ ->
            case eredis:q(ProcName, Cmd) of
                {ok, undefined} ->
                    <<>>;
                {ok, V} ->
                    V;
                _Err -> <<>>
            end
    end.


to_term(<<>>) ->
    undefined;
to_term(undefined) ->
    undefined;
to_term(Bin) ->
    binary_to_term(Bin).
